<?php

namespace Reezy\AlipaySdk;

use function base64_decode;
use function base64_encode;
use function chunk_split;
use function is_array;
use function is_string;
use function ksort;
use function openssl_sign;
use function openssl_verify;
use function str_replace;
use function strlen;
use function strpos;
use function strrpos;
use function substr;
use function trim;
use const OPENSSL_ALGO_SHA256;
use const PHP_EOL;

class SignUtil
{
    const RSA_PRIVATE = 1;
    const RSA_PUBLIC = 2;

    /**
     * @param array $params 待签名的参数
     * @param string $privateKey 应用私钥
     * @return string
     */
    public static function sign(array $params, string $privateKey)
    {
        if (empty($privateKey)) {
            throw new AlipaySdkException('无效的RSA私钥');
        }
        $content = self::getSignContent($params);
        $pem = self::getPemKey($privateKey, self::RSA_PRIVATE);
        if (!openssl_sign($content, $sign, $pem, OPENSSL_ALGO_SHA256)) {
            throw new AlipaySdkException('签名失败');
        }
        return base64_encode($sign);
    }

    /**
     * @param string|array  $content 待验签的内容
     * @param string        $sign 签名值的Base64串
     * @param string        $publicKey 支付宝公钥
     * @return bool
     */
    public static function verify($content, string $sign, string $publicKey)
    {
        if (empty($publicKey)) {
            throw new AlipaySdkException('无效的支付宝公钥');
        }
        $pem = self::getPemKey($publicKey, self::RSA_PUBLIC);

        if (is_array($content)) {
            unset($content['sign']);
            unset($content['sign_type']);
            $content = self::getSignContent($content);
        }
        return openssl_verify($content, base64_decode($sign), $pem, OPENSSL_ALGO_SHA256) === 1;
    }

    /**
     * 将 key 转换成 pem 格式
     * @param string $key 密钥
     * @param int $type 密钥类型
     * @return string
     */
    public static function getPemKey(string $key, int $type = self::RSA_PUBLIC)
    {
        if ($type === self::RSA_PUBLIC) {
            $header = '-----BEGIN PUBLIC KEY-----';
            $footer = '-----END PUBLIC KEY-----';
        } else {
            $header = '-----BEGIN RSA PRIVATE KEY-----';
            $footer = '-----END RSA PRIVATE KEY-----';
        }
        $key = str_replace([PHP_EOL, $header, $footer], '', $key);
        return $header . PHP_EOL . chunk_split($key, 64, PHP_EOL) . $footer;
    }

    public static function parseSignContent($responseContent, $bodyKey)
    {
        $key = "\"$bodyKey\":";
        $startIndex = strpos($responseContent, $key) + strlen($key);
        $endIndex = strrpos($responseContent, ',"alipay_cert_sn"');
        if (!$endIndex) {
            $endIndex = strrpos($responseContent, ',"sign"');
        }
        if ($endIndex <= $startIndex) {
            return null;
        }
        return substr($responseContent, $startIndex, $endIndex - $startIndex);
    }

    public static function getSignContent($params)
    {
        ksort($params);

        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === self::checkEmpty($v) && "@" != substr($v, 0, 1)) {

                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }

        unset ($k, $v);
        return $stringToBeSigned;
    }

    private static function checkEmpty($value)
    {
        if (!isset($value)) return true;
        if ($value === null) return true;
        if (trim($value) === "") return true;
        return false;
    }
}